﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using MockupDesigner.AppItems;
using MockupDesigner.AppItems.Documents;
using MockupDesigner.AppItems.Commands;
using System.Windows;
using MockupDesigner.Dialogs;
using System.Windows.Controls;
using System.IO;
using MockupDesigner.AppItems.Services;
using System.Diagnostics;
using System.Windows.Markup;
using System.Xml;

namespace MockupDesigner
{
    public partial class Window1
    {
        private CommandProcessor commandProcessor = new CommandProcessor();

        private DesignerCanvas GetCurrentDesigner()
        {
            return ((Document)DocumentsTC.SelectedContent).DesignerCanvas;
        }

        public void SetCommandBindings()
        {
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, New_Executed));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, Open_Executed));
            this.CommandBindings.Add(new CommandBinding(MDCommands.CloseDocument, CloseDocument_Executed));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, Save_Executed));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.SaveAs, SaveAs_Executed));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, Print_Executed));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, Close_Executed));
            this.CommandBindings.Add(new CommandBinding(MDCommands.ExportAsImage, ExportAsImage_Executed));
            this.CommandBindings.Add(new CommandBinding(MDCommands.ExportMetadatas, ExportMetadatas_Executed));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AddToCustomControls, AddToCustomControls_Executed));

            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, Cut_Executed, Cut_Enabled));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, Copy_Executed, Copy_Enabled));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, Paste_Executed, Paste_Enabled));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, Delete_Executed, Delete_Enabled));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, Undo_Executed, Undo_Enabled));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, Redo_Executed, Redo_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.Group, Group_Executed, Group_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.Ungroup, Ungroup_Executed, Ungroup_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.BringForward, BringForward_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.BringToFront, BringToFront_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.SendBackward, SendBackward_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.SendToBack, SendToBack_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignTop, AlignTop_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignVerticalCenters, AlignVerticalCenters_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignBottom, AlignBottom_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignLeft, AlignLeft_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignHorizontalCenters, AlignHorizontalCenters_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.AlignRight, AlignRight_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.DistributeHorizontal, DistributeHorizontal_Executed, Distribute_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.DistributeVertical, DistributeVertical_Executed, Distribute_Enabled));
            this.CommandBindings.Add(new CommandBinding(MDCommands.About, About_Executed));
            this.CommandBindings.Add(new CommandBinding(MDCommands.SelectAll, SelectAll_Executed));

        }

        #region Commands

        #region New Command

        private void New_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new NewCommand(), sender, null);
        }

        public void AddDocument(Document document)
        {
            List<Document> items = new List<Document>(this.DocumentsTC.ItemsSource as IList<Document>);
            if (!string.IsNullOrEmpty(document.FileName))
            {
                int index = items.FindIndex(doc => doc.FileName == document.FileName);
                if (index != -1)
                {
                    this.DocumentsTC.SelectedIndex = index;
                    return;
                }
            }
            items.Add(document);

            this.DocumentsTC.ItemsSource = items;
            if (!string.IsNullOrEmpty(document.FileName) && !document.IsSaved && items.Count == 2 && string.IsNullOrEmpty(items[0].FileName))
            {
                this.CloseDoc(items[0]);
            }
            this.DocumentsTC.SelectedIndex = items.Count - 1;
        }

        public void AddNewDocument()
        {
            AddDocument(new Document() { DocumentName = string.Concat(Document.NEW_FILE_DEFAULT_NAME, Document.DocCounter++), DesignerCanvas = new DesignerCanvas(), IsSaved = true });
        }

        #endregion

        #region Open Command

        private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new OpenCommand(), sender, null);
        }

        #endregion

        #region Save Command

        private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new SaveCommand(), sender, (Document)DocumentsTC.SelectedItem);
        }

        #endregion

        #region SaveAs Command

        private void SaveAs_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new SaveCommand() { isSaveAs = true }, sender, (Document)DocumentsTC.SelectedItem);
        }

        #endregion

        #region ExportAsImage Command

        private void ExportAsImage_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new ExportAsImageCommand(), GetCurrentDesigner(), (Document)DocumentsTC.SelectedItem);
        }

        #endregion

        #region ExportMetadatas Command

        private void ExportMetadatas_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new ExportMetadatasCommand(), GetCurrentDesigner(), (Document)DocumentsTC.SelectedItem);
        }

        #endregion

        #region Print Command

        private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new PrintCommand(), sender, (Document)DocumentsTC.SelectedItem);
        }

        #endregion

        #region About Command

        private void About_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            new AboutBox() { Owner = this }.ShowDialog();
        }

        #endregion

        #region CloseDocument Command

        private void CloseDocument_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            commandProcessor.ExecuteCommand(new CloseDocumentCommand(), sender,
                e.Parameter == null ? (Document)DocumentsTC.SelectedItem : (Document)e.Parameter);
        }

        public void CloseDoc(Document document)
        {
            if (!CheckDocuments(new List<Document>() { document }))
                return;

            Document selectedDocument = (Document)DocumentsTC.SelectedItem;
            IList<Document> items = new List<Document>(this.DocumentsTC.ItemsSource as IList<Document>);
            int index = items.IndexOf(document);
            items.Remove(document);
            DocumentsTC.ItemsSource = items;
            if (items.Count == 0)
            {
                AddNewDocument();
                return;
            }
            if (selectedDocument != document)
                DocumentsTC.SelectedItem = selectedDocument;
        }

        private bool CheckDocuments(List<Document> documents)
        {
            IList<Document> documentsToCheck = documents.FindAll(doc => !doc.IsSaved);
            if (documentsToCheck.Count > 0)
            {
                CloseWindow closeWindow = new CloseWindow() { Documents = documentsToCheck, Owner = this };
                closeWindow.ShowDialog();
                switch (closeWindow.Result)
                {
                    case MessageBoxResult.Yes:
                        foreach (Document document in documentsToCheck)
                        {
                            commandProcessor.ExecuteCommand(new SaveCommand(), this, document);
                            if (!document.IsSaved)
                                return false;
                        }
                        break;
                    case MessageBoxResult.Cancel:
                    case MessageBoxResult.None:
                        return false;
                    default:
                        return true;
                }
            }
            return true;
        }

        #endregion

        #region Close Command
        private void Close_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }

        void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = !CheckDocuments(new List<Document>(DocumentsTC.ItemsSource as IList<Document>));
            if (!e.Cancel)
                this.SaveToolboxes();
        }
        #endregion

        #region Copy Command

        private void Copy_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new CopyCommand(), canvas, null);
        }

        private void Copy_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count() > 0;
        }

        #endregion

        #region Paste Command

        private void Paste_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new PasteCommand(), canvas, null);
        }

        private void Paste_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = Clipboard.ContainsData(DataFormats.Xaml);
        }

        #endregion

        #region Delete Command

        private void Delete_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new DeleteCommand(), canvas, null);
        }

        private void Delete_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count() > 0;
        }

        #endregion

        #region Undo Command

        private void Undo_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new UndoCommand(), canvas.CommandProcessor, null);
        }

        private void Undo_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().CommandProcessor.Undo_Enabled;
        }

        #endregion

        #region Redo Command

        private void Redo_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new RedoCommand(), canvas.CommandProcessor, null);
        }

        private void Redo_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().CommandProcessor.Redo_Enabled;
        }

        #endregion

        #region Cut Command

        private void Cut_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new CutCommand(), canvas);
        }

        private void Cut_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count() > 0;
        }

        #endregion

        #region Group Command

        private void Group_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new GroupCommand(), canvas, null);
        }

        private void Group_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            int count = (from item in GetCurrentDesigner().SelectionService.CurrentSelection.OfType<DesignerItem>()
                         where item.ParentID == Guid.Empty
                         select item).Count();

            e.CanExecute = count > 1;
        }

        #endregion

        #region Ungroup Command

        private void Ungroup_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new UngroupCommand(), canvas, null);
        }

        private void Ungroup_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            var groupedItem = from item in GetCurrentDesigner().SelectionService.CurrentSelection.OfType<DesignerItem>()
                              where item.ParentID != Guid.Empty
                              select item;


            e.CanExecute = groupedItem.Count() > 0;
        }

        #endregion

        #region BringForward Command

        private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new BackwardForwardCommand(), canvas, MovementType.Forward);
        }

        private void Order_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count() > 0;
        }

        #endregion

        #region BringToFront Command

        private void BringToFront_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new ToBackFrontCommand(), canvas, OppositeType.ToFront);
        }

        #endregion

        #region SendBackward Command

        private void SendBackward_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new BackwardForwardCommand(), canvas, MovementType.Backward);
        }

        #endregion

        #region SendToBack Command

        private void SendToBack_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new ToBackFrontCommand(), canvas, OppositeType.ToBack);
        }

        #endregion

        #region AlignTop Command

        private void AlignTop_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.Top);
        }

        private void Align_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count > 0;
        }

        #endregion

        #region AlignVerticalCenters Command

        private void AlignVerticalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.VerticalCenter);
        }

        #endregion

        #region AlignBottom Command

        private void AlignBottom_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.Bottom);
        }

        #endregion

        #region AlignLeft Command

        private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.Left);
        }

        #endregion

        #region AlignHorizontalCenters Command

        private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.HorizontalCenter);
        }

        #endregion

        #region AlignRight Command

        private void AlignRight_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new AlignCommand(), canvas, AlignType.Right);
        }

        #endregion

        #region DistributeHorizontal Command

        private void DistributeHorizontal_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new DistributeCommand(), canvas, DistributeType.Horizontal);
        }

        private void Distribute_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetCurrentDesigner().SelectionService.CurrentSelection.Count > 0;
        }

        #endregion

        #region DistributeVertical Command

        private void DistributeVertical_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DesignerCanvas canvas = GetCurrentDesigner();
            canvas.CommandProcessor.ExecuteCommand(new DistributeCommand(), canvas, DistributeType.Vertical);
        }

        #endregion

        #region SelectAll Command

        private void SelectAll_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            GetCurrentDesigner().SelectionService.SelectAll();
        }

        #endregion

        #region Toolboxes Commands

        private static ConfigFileSerializationService configFileSerializationService = new ConfigFileSerializationService();
        private static string APP_PATH = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
        private const string CONFIG_FILENAME = "MockupDesigner.config.xml";

        #region AddToCustomControls Command
        private void AddToCustomControls_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            AddControlToToolboxWindow addWindow = new AddControlToToolboxWindow() { Owner = this };
            foreach (CustomToolbox ctb in customToolboxes)
                addWindow.Toolboxes.Add(ctb);
            bool? result = addWindow.ShowDialog();
            if (result.HasValue && result.Value && addWindow.SelectedToolboxes.Count > 0)
            {
                foreach (CustomToolbox ctb in addWindow.SelectedToolboxes)
                {
                    if (!(e.Parameter is DesignerItem))
                        continue;
                    DesignerCanvas canvas = (DesignerCanvas)((DesignerItem)e.Parameter).Parent;
                    IList<DesignerItem> groupItems = canvas.SelectionService.CurrentSelection.OfType<DesignerItem>().Where(di => di.IsGroup).ToList();
                    var itemsToRemove = from item1 in canvas.SelectionService.CurrentSelection.OfType<DesignerItem>()
                                        join item2 in canvas.SelectionService.CurrentSelection.OfType<DesignerItem>().Where(di => di.IsGroup)
                                        on item1.ParentID equals item2.ID
                                        select item1;
                    IList<DesignerItem> items = new List<DesignerItem>(canvas.SelectionService.CurrentSelection.OfType<DesignerItem>().ToList());
                    foreach (DesignerItem item in itemsToRemove)
                        items.Remove(item);

                    foreach (DesignerItem di in items)
                        AddNewItemInToolBox(addWindow.ControlToolTip, ctb, di);
                }
                customToolboxes = new List<CustomToolbox>(addWindow.Toolboxes);
                SaveToolboxes();
                LoadToolboxes();
            }
        }

        private void AddNewItemInToolBox(string tooltip, CustomToolbox ctb, DesignerItem designerItem)
        {
            ToolboxItem tb = new ToolboxItem() { Height = 80, Width = 100, IsCustom = true, ToolTip = tooltip };
            if (designerItem.IsGroup)
            {
                StackPanel st = new StackPanel();
                List<DesignerItem> children = new List<DesignerItem>();
                foreach (DesignerItem item in ((DesignerCanvas)designerItem.Parent).SelectionService.CurrentSelection.OfType<DesignerItem>().Where(di => di.ParentID == designerItem.ID))
                {
                    DesignerItem newItem = (DesignerItem)XamlReader.Load(XmlReader.Create(new StringReader(XamlWriter.Save(item))));
                    newItem.IsSelected = false;
                    newItem.ParentID = Guid.Empty;
                    newItem.IsInToolbox = true;
                    children.Add(newItem);
                }
                children.Sort(delegate(DesignerItem di1, DesignerItem di2)
                {
                    return Canvas.GetTop(di1).CompareTo(Canvas.GetTop(di2));
                });
                double minTop = Canvas.GetTop(children[0]);
                double maxTop = Canvas.GetTop(children[1]);
                children.Sort(delegate(DesignerItem di1, DesignerItem di2)
                {
                    return Canvas.GetLeft(di1).CompareTo(Canvas.GetLeft(di2));
                });
                double minLeft = Canvas.GetLeft(children[0]);
                children.OrderByDescending(di => di.Width);
                foreach (DesignerItem item in children)
                {
                    Canvas.SetTop(item, Canvas.GetTop(item) - minTop);
                    Canvas.SetLeft(item, Canvas.GetLeft(item) - minLeft);
                    st.Children.Add(item);
                }
                tb.Content = new DesignerItem() { IsInToolbox = true, Content = st };
            }
            else
            {
                DesignerItem newItem = (DesignerItem)XamlReader.Load(XmlReader.Create(new StringReader(XamlWriter.Save(designerItem))));
                newItem.IsInToolbox = true;
                newItem.IsSelected = false;
                tb.Content = XamlReader.Load(XmlReader.Create(new StringReader(XamlWriter.Save(newItem))));
            }
            ctb.Items.Add(tb);
        }


        #endregion

        private IList<CustomToolbox> customToolboxes;
        private void LoadToolboxes()
        {
            customToolboxes = configFileSerializationService.DeserializeToolboxesFromFile(Path.Combine(APP_PATH, CONFIG_FILENAME));
            if (ToolboxesSP.Children.Count > 5)
                ToolboxesSP.Children.RemoveRange(5, ToolboxesSP.Children.Count - 5);
            foreach (CustomToolbox ctb in customToolboxes)
            {
                Expander expander = new Expander() { Header = ctb.ToolboxName };
                expander.Content = ctb;
                expander.IsExpanded = ctb.IsExpanded;
                expander.Expanded += new RoutedEventHandler(expander_Expanded);
                expander.PreviewMouseDown += new MouseButtonEventHandler(expander_PreviewMouseDown);
                expander.CommandBindings.Add(new CommandBinding(MDCommands.DeleteToolbox, DeleteToolbox_Executed));
                ToolboxesSP.Children.Add(expander);
            }
        }

        private void SaveToolboxes()
        {
            string configPath = Path.Combine(APP_PATH, CONFIG_FILENAME);
            configFileSerializationService.SerializeToolboxesInFile(customToolboxes, Path.Combine(APP_PATH, CONFIG_FILENAME));
        }

        private void DeleteToolbox_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            customToolboxes.Remove((CustomToolbox)((Expander)sender).Content);
            SaveToolboxes();
            LoadToolboxes();
        }

        


        #endregion

        #endregion
    }
}
